home *** CD-ROM | disk | FTP | other *** search
- /* $Id: pl-trace.c,v 1.41 1998/04/15 15:17:11 jan Exp $
-
- Copyright (c) 1990 Jan Wielemaker. All rights reserved.
- See ../LICENCE to find out about your rights.
- jan@swi.psy.uva.nl
-
- Purpose: tracer
- */
-
- #include "pl-incl.h"
- #include "pl-ctype.h"
-
- int trace_continuation; /* how to continue? */
-
- #define W_PRINT 1 /* print/1 for displaying goal */
- #define W_WRITE 2 /* write/1 */
- #define W_WRITEQ 3 /* writeq/1 */
- #define W_DISPLAY 4 /* display/1 */
-
- #define TRACE_FIND_NONE 0
- #define TRACE_FIND_ANY 1
- #define TRACE_FIND_NAME 2
- #define TRACE_FIND_TERM 3
-
- typedef struct find_data_tag
- { int port; /* Port to find */
- bool searching; /* Currently searching? */
- int type; /* TRACE_FIND_* */
- union
- { atom_t name; /* Name of goal to find */
- struct
- { functor_t functor; /* functor of the goal */
- Record term; /* Goal to find */
- } term;
- } goal;
- } find_data;
-
- #define PrologRef(fr) ((Word)fr - (Word)lBase)
- #define FrameRef(w) ((LocalFrame)((Word)lBase + w))
-
- #ifdef O_DEBUGGER
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- This module defines the tracer and interrupt handler that allows the
- user to break the normal Prolog execution. The tracer is written in C,
- but before taking action it calls Prolog. This mechanism allows the
- user to intercept and redefine the tracer.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- /* Frame <-> Prolog integer */
- forwards LocalFrame redoFrame(LocalFrame, Code *PC);
- forwards int traceAction(char *, int, LocalFrame, bool);
- forwards void helpTrace(void);
- #ifdef O_INTERRUPT
- forwards void helpInterrupt(void);
- #endif
- forwards bool hasAlternativesFrame(LocalFrame);
- forwards void alternatives(LocalFrame);
- forwards void listProcedure(Definition);
- forwards int traceInterception(LocalFrame, LocalFrame, int, Code);
- forwards void writeFrameGoal(LocalFrame frame, int how);
- forwards void interruptHandler(int sig);
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- redoFrame() returns the latest skipped frame or NULL if no such frame
- exists. This is used to give the redo port of the goal skipped rather
- than the redo port of some subgoal of this port.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- static LocalFrame
- redoFrame(LocalFrame fr, Code *PC)
- { while( fr && false(fr, FR_SKIPPED))
- { *PC = fr->programPointer;
- fr = parentFrame(fr);
- }
-
- return fr;
- }
-
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- canUnifyTermWithGoal() is used to check whether the given frame satisfies
- the /search specification. This function cannot use the `neat' interface
- as the record is not in the proper format.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- static bool
- canUnifyTermWithGoal(LocalFrame fr)
- { find_data *find = LD->trace.find;
-
- switch(find->type)
- { case TRACE_FIND_ANY:
- succeed;
- case TRACE_FIND_NAME:
- return find->goal.name == fr->predicate->functor->name;
- case TRACE_FIND_TERM:
- { if ( find->goal.term.functor == fr->predicate->functor->functor )
- { fid_t cid = PL_open_foreign_frame();
- term_t t = PL_new_term_ref();
- Word a, b;
- int arity = fr->predicate->functor->arity;
- int rval = TRUE;
-
- copyRecordToGlobal(t, find->goal.term.term);
- a = valTermRef(t);
- deRef(a);
- a = argTermP(*a, 0);
- b = argFrameP(fr, 0);
- while( arity-- > 0 )
- { if ( !can_unify(a++, b++) )
- { rval = FALSE;
- break;
- }
- }
-
- PL_discard_foreign_frame(cid);
- return rval;
- }
-
- fail;
- }
- default:
- assert(0);
- fail;
- }
- }
-
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Toplevel of the tracer. This function is called from the WAM
- interpreter. It can take care of most of the tracer actions itself,
- except if the execution path is to be changed. For this reason it
- returns to the WAM interpreter how to continue the execution:
-
- ACTION_CONTINUE: Continue normal
- ACTION_FAIL: Go to the fail port of this goal
- ACTION_RETRY: Redo the current goal
- ACTION_IGNORE: Go to the exit port of this goal
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- int
- tracePort(LocalFrame frame, LocalFrame bfr, int port, Code PC)
- { int OldOut;
- extern int Output;
- int action = ACTION_CONTINUE;
- Definition def = frame->predicate;
- LocalFrame fr;
-
- if ( (true(frame, FR_NODEBUG) && !(SYSTEM_MODE)) || /* hidden */
- debugstatus.suspendTrace ) /* called back */
- return ACTION_CONTINUE;
-
- if ( port == FAIL_PORT )
- Undo(frame->mark);
-
- /* trace/[1,2] */
- if ( true(def, TRACE_CALL|TRACE_REDO|TRACE_EXIT|TRACE_FAIL) &&
- !(port & (BREAK_PORT|CUT_PORT)) )
- { char *fmt = NULL;
-
- switch(port)
- { case CALL_PORT:
- if ( true(def, TRACE_CALL) )
- fmt = "T Call: (%3ld) ";
- break;
- case REDO_PORT:
- if ( true(def, TRACE_REDO) )
- fmt = "T Redo: (%3ld) ";
- break;
- case EXIT_PORT:
- if ( true(def, TRACE_EXIT) )
- fmt = "T Exit: (%3ld) ";
- break;
- case FAIL_PORT:
- if ( true(def, TRACE_FAIL) )
- fmt = "T Fail: (%3ld) ";
- break;
- }
- if ( fmt )
- { Putf(fmt, levelFrame(frame));
- writeFrameGoal(frame, debugstatus.style);
- Put('\n');
- }
- }
-
- if ( (port != BREAK_PORT) &&
- /*(port != EXCEPTION_PORT) &&*/
- ((!debugstatus.tracing &&
- (false(def, SPY_ME) || (port & CUT_PORT))) || /* non-tracing */
- debugstatus.skiplevel < levelFrame(frame) || /* skipped */
- ((port & (BREAK_PORT|CUT_PORT)) &&
- ((debugstatus.skiplevel == levelFrame(frame)) ||
- true(def, HIDE_CHILDS))) || /* also skipped */
- false(def, TRACE_ME) || /* non-tracing */
- (!(debugstatus.visible & port)) || /* wrong port */
- (port == REDO_PORT && (debugstatus.skiplevel == levelFrame(frame) ||
- (true(def, SYSTEM) && !SYSTEM_MODE)
- ))) ) /* redos */
- return ACTION_CONTINUE;
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Give a trace on the skipped goal for a redo.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- { Code pc2;
-
- if ( port == REDO_PORT && debugstatus.skiplevel == VERY_DEEP &&
- (fr = redoFrame(frame, &pc2)) != NULL )
- { debugstatus.skiplevel--; /* avoid a loop */
- switch( tracePort(fr, bfr, REDO_PORT, pc2) )
- { case ACTION_CONTINUE:
- if ( debugstatus.skiplevel < levelFrame(frame) )
- return ACTION_CONTINUE;
- break;
- case ACTION_RETRY:
- case ACTION_IGNORE:
- case ACTION_FAIL:
- Putf("Action not yet implemented here\n");
- break;
- }
- }
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- We are in searching mode; should we actually give this port?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- if ( LD->trace.find && LD->trace.find->searching )
- { DEBUG(2, Sdprintf("Searching\n"));
-
- if ( (port & LD->trace.find->port) && canUnifyTermWithGoal(frame) )
- { LD->trace.find->searching = FALSE; /* Got you */
- } else
- { return ACTION_CONTINUE; /* Continue the search */
- }
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Do the Prolog trace interception.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- action = traceInterception(frame, bfr, port, PC);
- if ( action >= 0 )
- return action;
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- All failed. Things now are upto the normal Prolog tracer.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- action = ACTION_CONTINUE;
- OldOut = Output;
- Output = 1;
-
- again:
- Put( true(def, SPY_ME) ? '*' : ' ' );
- Put( true(def, METAPRED) ? '^' : ' ');
-
- switch(port)
- { case CALL_PORT: Putf(" Call: "); break;
- case REDO_PORT: Putf(" Redo: "); break;
- case FAIL_PORT: Putf(" Fail: "); break;
- case EXIT_PORT: Putf(" Exit: "); break;
- case UNIFY_PORT: Putf(" Unify: "); break;
- case BREAK_PORT: Putf(" Break: "); break;
- case EXCEPTION_PORT: Putf(" Exception: "); break;
- case CUT_CALL_PORT: Putf(" Cut call: "); break;
- case CUT_EXIT_PORT: Putf(" Cut exit: "); break;
- }
- Putf("(%3ld) ", levelFrame(frame));
- writeFrameGoal(frame, debugstatus.style);
-
- if (debugstatus.leashing & port)
- { char buf[LINESIZ];
-
- debugstatus.skiplevel = VERY_DEEP;
- debugstatus.tracing = TRUE;
-
- Putf(" ? ");
- pl_flush();
- if ( GD->cmdline.notty )
- { buf[0] = EOS;
- readLine(buf);
- } else
- { buf[0] = getSingleChar();
- buf[1] = EOS;
- if ( isDigit(buf[0]) || buf[0] == '/' )
- { Putf(buf);
- readLine(buf);
- }
- }
- if ((action = traceAction(buf, port, frame, GD->cmdline.notty ? FALSE : TRUE))
- == ACTION_AGAIN)
- goto again;
- } else
- Put('\n');
- Output = OldOut;
-
- return action;
- }
-
-
- static int
- setupFind(char *buf)
- { long rval;
- char *s;
- int port = 0;
-
- for(s = buf; *s && isBlank(*s); s++) /* Skip blanks */
- ;
- if ( *s == EOS ) /* No specification: repeat */
- { if ( !LD->trace.find || !LD->trace.find->port )
- { Putf("[No previous search]\n");
- fail;
- }
- LD->trace.find->searching = TRUE;
- succeed;
- }
- for( ; *s && !isBlank(*s); s++ ) /* Parse the port specification */
- { switch( *s )
- { case 'c': port |= CALL_PORT; continue;
- case 'e': port |= EXIT_PORT; continue;
- case 'r': port |= REDO_PORT; continue;
- case 'f': port |= FAIL_PORT; continue;
- case 'u': port |= UNIFY_PORT; continue;
- case 'a': port |= CALL_PORT|REDO_PORT|FAIL_PORT|EXIT_PORT|UNIFY_PORT;
- continue;
- default: Putf("[Illegal port specification]\n");
- fail;
- }
- }
- for( ; *s && isBlank(*s); s++) /* Skip blanks */
- ;
-
- if ( *s == EOS ) /* Nothing is a variable */
- { s = buf;
- buf[0] = '_',
- buf[1] = EOS;
- }
-
- { fid_t cid = PL_open_foreign_frame();
- term_t t = PL_new_term_ref();
- FindData find;
-
- if ( !(find = LD->trace.find) )
- find = LD->trace.find = allocHeap(sizeof(find_data));
-
- seeString(s);
- rval = pl_read(t);
- seenString();
-
- if ( rval == FALSE )
- { PL_discard_foreign_frame(cid);
- fail;
- }
-
- if ( find->type == TRACE_FIND_TERM && find->goal.term.term )
- freeRecord(find->goal.term.term);
-
- if ( PL_is_variable(t) )
- { find->type = TRACE_FIND_ANY;
- } else if ( PL_get_atom(t, &find->goal.name) )
- { find->type = TRACE_FIND_NAME;
- } else if ( PL_get_functor(t, &find->goal.term.functor) )
- { find->type = TRACE_FIND_TERM;
- find->goal.term.term = compileTermToHeap(t);
- } else
- { Putf("[Illegal goal specification]\n");
- fail;
- }
-
- find->port = port;
- find->searching = TRUE;
-
- DEBUG(2, Sdprintf("setup ok, port = 0x%x, goal = ", port);
- pl_write(t);
- Sdprintf("\n") );
-
- PL_discard_foreign_frame(cid);
- }
-
- succeed;
- }
-
-
- static int
- traceAction(char *cmd, int port, LocalFrame frame, bool interactive)
- { int num_arg; /* numeric argument */
- char *s;
-
- #define FeedBack(msg) { if (interactive) { if (cmd[1] != EOS) \
- Putf("\n"); \
- else \
- Putf(msg); } }
- #define Warn(msg) { if (interactive) Putf(msg); else warning(msg); }
- #define Default (-1)
-
- for(s=cmd; *s && isBlank(*s); s++)
- ;
- if ( isDigit(*s) )
- { num_arg = strtol(s, &s, 10);
-
- while(isBlank(*s))
- s++;
- } else
- num_arg = Default;
-
- switch( *s )
- { case 'a': FeedBack("abort\n");
- pl_abort();
- case 'b': FeedBack("break\n");
- pl_break();
- return ACTION_AGAIN;
- case '/': FeedBack("/");
- pl_flush();
- if ( setupFind(&s[1]) )
- { clear(frame, FR_SKIPPED);
- return ACTION_CONTINUE;
- }
- return ACTION_AGAIN;
- case '.': if ( LD->trace.find &&
- LD->trace.find->type != TRACE_FIND_NONE )
- { FeedBack("repeat search\n");
- LD->trace.find->searching = TRUE;
- clear(frame, FR_SKIPPED);
- return ACTION_CONTINUE;
- } else
- { Warn("No previous search\n");
- }
- return ACTION_AGAIN;
- case EOS:
- case ' ':
- case '\n':
- case 'c': FeedBack("creep\n");
- clear(frame, FR_SKIPPED);
- return ACTION_CONTINUE;
- case '\04':
- case EOF: FeedBack("EOF: ");
- case 'e': FeedBack("exit\n");
- Halt(0);
- case 'f': FeedBack("fail\n");
- return ACTION_FAIL;
- case 'i': if (port & (CALL_PORT|REDO_PORT|FAIL_PORT))
- { FeedBack("ignore\n");
- return ACTION_IGNORE;
- } else
- Warn("Can't ignore goal at this port\n");
- return ACTION_CONTINUE;
- case 'r': if (port & (REDO_PORT|FAIL_PORT|EXIT_PORT|EXCEPTION_PORT))
- { FeedBack("retry\n[retry]\n");
- return ACTION_RETRY;
- } else
- Warn("Can't retry at this port\n");
- return ACTION_CONTINUE;
- case 's': FeedBack("skip\n");
- set(frame, FR_SKIPPED);
- debugstatus.skiplevel = levelFrame(frame);
- return ACTION_CONTINUE;
- case 'u': FeedBack("up\n");
- debugstatus.skiplevel = levelFrame(frame) - 1;
- return ACTION_CONTINUE;
- case 'w': FeedBack("write\n");
- debugstatus.style = W_WRITEQ;
- return ACTION_AGAIN;
- case 'p': FeedBack("print\n");
- debugstatus.style = W_PRINT;
- return ACTION_AGAIN;
- case 'd': FeedBack("write canonical\n");
- debugstatus.style = W_DISPLAY;
- return ACTION_AGAIN;
- case 'l': FeedBack("leap\n");
- debugstatus.tracing = FALSE;
- return ACTION_CONTINUE;
- case 'n': FeedBack("no debug\n");
- debugstatus.debugging = FALSE;
- debugstatus.tracing = FALSE;
- return ACTION_CONTINUE;
- case 'g': FeedBack("goals\n");
- backTrace(frame, num_arg == Default ? 5 : num_arg);
- return ACTION_AGAIN;
- case 'A': FeedBack("alternatives\n");
- alternatives(frame);
- return ACTION_AGAIN;
- case 'C': debugstatus.showContext = 1 - debugstatus.showContext;
- if ( debugstatus.showContext == TRUE )
- { FeedBack("Show context\n");
- } else
- { FeedBack("No show context\n");
- }
- return ACTION_AGAIN;
- case 'L': FeedBack("Listing");
- listProcedure(frame->predicate);
- return ACTION_AGAIN;
- case '+': FeedBack("spy\n");
- set(frame->predicate, SPY_ME);
- return ACTION_AGAIN;
- case '-': FeedBack("no spy\n");
- clear(frame->predicate, SPY_ME);
- return ACTION_AGAIN;
- case '?':
- case 'h': helpTrace();
- return ACTION_AGAIN;
- case 'D': GD->debug_level = num_arg;
- FeedBack("Debug level\n");
- return ACTION_AGAIN;
- default: Warn("Unknown option (h for help)\n");
- return ACTION_AGAIN;
- }
- }
-
- static void
- helpTrace(void)
- { Putf("Options:\n");
- Putf("+: spy -: no spy\n");
- Putf("/c|e|r|f|u|a} goal: find .: repeat find\n");
- Putf("a: abort A: alternatives\n");
- Putf("b: break c (return, space): creep\n");
- Putf("d: display e: exit\n");
- Putf("f: fail [depth] g: goals\n");
- Putf("h (?): help i: ignore\n");
- Putf("l: leap L: listing\n");
- Putf("n: no debug p: print\n");
- Putf("r: retry s: skip\n");
- Putf("u: up w: write\n");
- Putf("C: toggle show context\n");
- #if O_DEBUG
- Putf("[level] D: set system debug level\n");
- #endif
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Write goal of stack frame. First a term representing the goal of the
- frame is constructed. Trail and global stack are marked and undone to
- avoid garbage on the global stack.
-
- Trick, trick, O big trick ... In order to print the goal we create a
- term for it (otherwise we would have to write a special version of
- write/1, etc. for stack frames). A small problem arises: if the frame
- holds a variable we will make a reference to the new term, thus printing
- the wrong variable: variables sharing in a clause does not seem to share
- any longer in the tracer (Anjo Anjewierden discovered this ackward
- feature of the tracer). The solution is simple: we make the reference
- pointer the other way around. Normally references should never go from
- the global to the local stack as the local stack frame might cease to
- exists before the global frame. In this case this does not matter as
- the local stack frame definitely survives the tracer (measuring does not
- always mean influencing in computer science).
-
- For the above reason, the code below uses low-level manipulation rather
- than normal unification, etc.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- static void
- writeFrameGoal(LocalFrame frame, int how)
- { Definition def = frame->predicate;
- Word argv = argFrameP(frame, 0);
- int argc = def->functor->arity;
- int debugSave = debugstatus.debugging;
- fid_t cid = PL_open_foreign_frame();
- term_t goal = PL_new_term_ref();
-
- if ( debugstatus.showContext )
- Putf("[%s] ", stringAtom(contextModule(frame)->name));
- if ( def->module != MODULE_user &&
- (false(def->module, SYSTEM) || SYSTEM_MODE))
- Putf("%s:", stringAtom(def->module->name));
-
- PL_unify_functor(goal, def->functor->functor);
- if ( argc > 0 )
- { Word argp = valTermRef(goal);
- int i;
-
- deRef(argp);
- argp = argTermP(*argp, 0);
-
- for(i=0; i<argc; i++)
- { Word a;
-
- deRef2(argv+i, a);
- *argp++ = (isVar(*a) ? makeRef(a) : *a);
- }
- }
-
- switch(how)
- { case W_PRINT:
- debugstatus.debugging = FALSE;
- if ( GD->bootsession )
- pl_write(goal);
- else
- pl_print(goal);
- debugstatus.debugging = debugSave;
- break;
- case W_WRITE:
- pl_write(goal);
- break;
- case W_WRITEQ:
- pl_writeq(goal);
- break;
- case W_DISPLAY:
- pl_write_canonical(goal);
- break;
- }
-
- PL_discard_foreign_frame(cid);
- }
-
- /* Write those frames on the stack that have alternatives left.
-
- ** Tue May 10 23:23:11 1988 jan@swivax.UUCP (Jan Wielemaker) */
-
- static void
- alternatives(LocalFrame frame)
- { for(; frame; frame = frame->backtrackFrame)
- { if (hasAlternativesFrame(frame) &&
- (false(frame, FR_NODEBUG) || SYSTEM_MODE) )
- { Putf(" [%3ld] ", levelFrame(frame));
- writeFrameGoal(frame, debugstatus.style);
- Put('\n');
- }
- }
- }
-
-
- static void
- listProcedure(Definition def)
- { fid_t cid = PL_open_foreign_frame();
- qid_t qid;
- extern int Output;
- int OldOut = Output;
- term_t argv = PL_new_term_refs(1);
- Procedure proc = lookupProcedure(FUNCTOR_listing1, MODULE_system);
-
- unify_definition(argv, def, 0, 0); /* module:name(args) */
- Output = 1;
- qid = PL_open_query(MODULE_user, PL_Q_NODEBUG, proc, argv);
- PL_next_solution(qid);
- PL_close_query(qid);
- Output = OldOut;
- PL_discard_foreign_frame(cid);
- }
-
-
- void
- backTrace(LocalFrame frame, int depth)
- { extern int Output;
- int OldOut = Output;
- LocalFrame same_proc_frame = NULL;
- Definition def = NULL;
- int same_proc = 0;
- int alien = FALSE;
-
- if ( frame == NULL )
- frame = environment_frame;
-
- Output = 1;
- for(; depth > 0 && frame;
- alien = (frame->parent == NULL), frame = parentFrame(frame))
- { if ( alien )
- Putf(" <Alien goal>\n");
-
- if ( frame->predicate == def )
- { if ( ++same_proc >= 10 )
- { if ( same_proc == 10 )
- Putf(" ...\n ...\n");
- same_proc_frame = frame;
- continue;
- }
- } else
- { if ( same_proc_frame != NULL )
- { if ( false(same_proc_frame, FR_NODEBUG) || SYSTEM_MODE )
- { Putf(" [%3ld] ", levelFrame(same_proc_frame));
- writeFrameGoal(same_proc_frame, debugstatus.style);
- depth--;
- Put('\n');
- }
- same_proc_frame = NULL;
- same_proc = 0;
- }
- def = frame->predicate;
- }
-
- if (false(frame, FR_NODEBUG) || SYSTEM_MODE)
- { Putf(" [%3ld] ", levelFrame(frame));
- writeFrameGoal(frame, debugstatus.style);
- depth--;
- Put('\n');
- }
- }
- Output = OldOut;
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Trace interception mechanism. Whenever the tracer wants to perform some
- action it will first call the users' Prolog predicate
- prolog_trace_interception/4, allowing the user to define his/her action.
- If this procedure succeeds the tracer assumes the trace action has been
- done and returns, otherwise the default C-defined trace actions are
- performed.
-
- This predicate is supposed to return one of the following atoms:
-
- continue simply continue (creep)
- fail fail this goal
- retry retry this goal
- ignore pretend this call succeeded
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- static int
- traceInterception(LocalFrame frame, LocalFrame bfr, int port, Code PC)
- { int rval = -1; /* Default C-action */
- predicate_t proc;
-
- proc = _PL_predicate("prolog_trace_interception", 4, "user",
- &GD->procedures.prolog_trace_interception4);
- if ( !proc->definition->definition.clauses )
- return rval;
-
- if ( !GD->bootsession && GD->debug_level == 0 )
- { fid_t cid = PL_open_foreign_frame();
- qid_t qid;
- term_t argv = PL_new_term_refs(4);
- term_t rarg = argv+3;
- atom_t portname = NULL_ATOM;
- functor_t portfunc = 0;
- int nodebug = FALSE;
-
- switch(port)
- { case CALL_PORT: portname = ATOM_call; break;
- case REDO_PORT: portname = ATOM_redo; break;
- case EXIT_PORT: portname = ATOM_exit; break;
- case FAIL_PORT: portname = ATOM_fail; break;
- case UNIFY_PORT: portname = ATOM_unify; break;
- case EXCEPTION_PORT: portname = ATOM_exception; break;
- case BREAK_PORT: portfunc = FUNCTOR_break1; break;
- case CUT_CALL_PORT: portfunc = FUNCTOR_cut_call1; break;
- case CUT_EXIT_PORT: portfunc = FUNCTOR_cut_exit1; break;
- default:
- assert(0);
- return rval;
- }
-
- if ( portname )
- PL_put_atom(argv, portname);
- else
- { int pcn;
-
- if ( PC && false(frame->predicate, FOREIGN) && frame->clause )
- pcn = PC - frame->clause->clause->codes;
- else
- pcn = 0;
-
- PL_unify_term(argv,
- PL_FUNCTOR, portfunc,
- PL_INTEGER, pcn);
- }
-
- PL_put_integer(argv+1, PrologRef(frame));
- PL_put_integer(argv+2, PrologRef(bfr));
- PL_put_variable(rarg);
-
- qid = PL_open_query(MODULE_user, PL_Q_NODEBUG, proc, argv);
- if ( PL_next_solution(qid) )
- { atom_t a;
-
- if ( PL_get_atom(rarg, &a) )
- { if ( a == ATOM_continue )
- rval = ACTION_CONTINUE;
- else if ( a == ATOM_nodebug )
- { rval = ACTION_CONTINUE;
- nodebug = TRUE;
- } else if ( a == ATOM_fail )
- rval = ACTION_FAIL;
- else if ( a == ATOM_retry )
- rval = ACTION_RETRY;
- else if ( a == ATOM_ignore )
- rval = ACTION_IGNORE;
- } else if ( PL_is_functor(rarg, FUNCTOR_retry1) )
- { long w;
- term_t arg = PL_new_term_ref();
-
- if ( PL_get_arg(1, rarg, arg) && PL_get_long(arg, &w) )
- { debugstatus.retryFrame = FrameRef(w);
- rval = ACTION_RETRY;
- } else
- warning("prolog_trace_interception/3: bad argument to retry/1");
- }
- }
- PL_close_query(qid);
- PL_discard_foreign_frame(cid);
-
- if ( nodebug )
- pl_nodebug();
-
- }
-
- return rval;
- }
-
- #endif /*O_DEBUGGER*/
-
- static bool
- hasAlternativesFrame(register LocalFrame frame)
- { ClauseRef cref;
-
- if ( true(frame, FR_CUT) )
- fail;
- if (true(frame->predicate, FOREIGN))
- succeed;
- for(cref = frame->clause; cref; cref = cref->next)
- if ( false(cref->clause, ERASED) )
- succeed;
- fail;
- }
-
- word
- pl_trace_continuation(term_t what)
- { return PL_get_integer(what, &trace_continuation);
- }
-
- void
- resetTracer(void)
- {
- #ifdef O_INTERRUPT
- #if defined(HAVE_SIGACTION) && defined(SA_RESTART) && defined(SA_NOMASK)
- struct sigaction set;
-
- memset(&set, 0, sizeof(set));
- set.sa_handler = interruptHandler;
- set.sa_flags = SA_RESTART|SA_NOMASK;
-
- sigaction(SIGINT, &set, NULL);
- #else
- #ifdef HAVE_SIGSET
- sigset(SIGINT, interruptHandler);
- #else
- #ifndef BSD_SIGNALS
- #define REINSTATE_INTERRUPT_HANDLER
- #endif
- signal(SIGINT, interruptHandler);
- #endif
- #endif
- #endif
-
- debugstatus.tracing =
- debugstatus.debugging = FALSE;
- debugstatus.suspendTrace = FALSE;
- debugstatus.skiplevel = 0;
- debugstatus.retryFrame = NULL;
- }
-
-
- #ifdef O_INTERRUPT
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Handling interrupts. We know we are not in critical code (see
- startCritical() and endCritical(), so the heap is consistent. The only
- problem can be that we are currently writing the arguments of the next
- goal above the local stack top pointer. To avoid problems we just
- increment the top pointer to point above the furthest argument.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- static void
- helpInterrupt(void)
- { Putf("Options:\n");
- Putf("a: abort b: break\n");
- Putf("c: continue e: exit\n");
- #ifdef O_DEBUGGER
- Putf("g: goals t: trace\n");
- #endif
- Putf("h (?): help\n");
-
- }
-
- static void
- interruptHandler(int sig)
- { extern int Output;
- int OldOut = Output;
- LocalFrame oldltop = lTop;
- Char c;
-
- if ( !GD->initialised )
- { Sfprintf(Serror, "Interrupt during startup. Cannot continue\n");
- Halt(1);
- }
-
- Output = 1;
- gc_status.blocked++;
- #if O_SHIFT_STACKS
- shift_status.blocked++;
- #endif
- lTop = (LocalFrame)addPointer(lTop, sizeof(struct localFrame) +
- MAXARITY * sizeof(word));
- again:
- Putf("\nAction (h for help) ? ");
- pl_flush();
- ResetTty(); /* clear pending input -- atoenne -- */
- c = getSingleChar();
-
- #ifdef REINSTATE_INTERRUPT_HANDLER
- #ifdef SIG_ACK
- signal(SIGINT, SIG_ACK);
- #else
- signal(SIGINT, interruptHandler); /* reinsert handler */
- #endif
- #endif
-
- #if defined(SIG_UNBLOCK)
- { sigset_t set;
-
- sigemptyset(&set);
- sigaddset(&set, SIGINT);
- sigprocmask(SIG_UNBLOCK, &set, NULL);
- }
- #endif
-
- switch(c)
- { case 'a': Putf("abort\n");
- pl_abort();
- break;
- case 'b': Putf("break\n");
- pl_break();
- goto again;
- case 'c': Putf("continue\n");
- break;
- case 04:
- case EOF: Putf("EOF: ");
- case 'e': Putf("exit\n");
- Halt(0);
- break;
- #ifdef O_DEBUGGER
- case 'g': Putf("goals\n");
- backTrace(environment_frame, 5);
- goto again;
- #endif /*O_DEBUGGER*/
- case 'h':
- case '?': helpInterrupt();
- goto again;
- #ifdef O_DEBUGGER
- case 't': Putf("trace\n");
- pl_trace();
- break;
- #endif /*O_DEBUGGER*/
- default: Putf("Unknown option (h for help)\n");
- goto again;
- }
- #if O_SHIFT_STACKS
- shift_status.blocked--;
- #endif
- gc_status.blocked--;
- Output = OldOut;
- lTop = oldltop;
- }
-
- #endif /*O_INTERRUPT*/
-
-
- void
- PL_interrupt(int sig)
- {
- #ifdef O_INTERRUPT
- interruptHandler(sig);
- #endif
- }
-
-
- void
- initTracer(void)
- { debugstatus.visible =
- debugstatus.leashing = CALL_PORT|FAIL_PORT|REDO_PORT|EXIT_PORT|
- BREAK_PORT|EXCEPTION_PORT;
- debugstatus.style = GD->bootsession ? W_WRITE : W_PRINT;
- debugstatus.showContext = FALSE;
-
- resetTracer();
- }
-
- /********************************
- * PROLOG PREDICATES *
- *********************************/
-
- #if O_DEBUGGER
-
- int
- tracemode(int doit, int *old)
- { if ( doit )
- doit = TRUE;
-
- if ( old )
- *old = debugstatus.tracing;
-
- if ( debugstatus.tracing != doit )
- { if ( doit )
- { debugstatus.debugging = TRUE;
- debugstatus.skiplevel = VERY_DEEP;
- if ( LD->trace.find )
- LD->trace.find->searching = FALSE;
- }
- debugstatus.tracing = doit;
- callEventHook(PLEV_TRACING, doit);
- }
-
- succeed;
- }
-
-
- int
- debugmode(int doit, int *old)
- { if ( doit )
- doit = TRUE;
-
- if ( old )
- *old = debugstatus.debugging;
-
- if ( debugstatus.debugging != doit )
- { if ( doit )
- debugstatus.skiplevel = VERY_DEEP;
- debugstatus.debugging = doit;
- callEventHook(PLEV_DEBUGGING, doit);
- }
-
- succeed;
- }
-
- #else /*O_DEBUGGER*/
-
- int
- tracemode(int doit, int *old)
- { succeed;
- }
-
- int
- debugmode(int doit, int *old)
- { succeed;
- }
-
- #endif
-
- word
- pl_trace()
- { return tracemode(TRUE, NULL);
- }
-
- word
- pl_notrace()
- { return tracemode(FALSE, NULL);
- }
-
- word
- pl_tracing()
- { return debugstatus.tracing;
- }
-
- word
- pl_debug()
- { return debugmode(TRUE, NULL);
- }
-
- word
- pl_nodebug()
- { tracemode(FALSE, NULL);
- debugmode(FALSE, NULL);
-
- succeed;
- }
-
- word
- pl_debugging()
- { return debugstatus.debugging;
- }
-
- word
- pl_skip_level(term_t old, term_t new)
- { atom_t a;
- long sl;
-
- if ( debugstatus.skiplevel == VERY_DEEP )
- { TRY(PL_unify_atom(old, ATOM_very_deep));
- } else
- { TRY(PL_unify_integer(old, debugstatus.skiplevel));
- }
-
- if ( PL_get_long(new, &sl) )
- { debugstatus.skiplevel = (unsigned long) sl;
- succeed;
- }
- if ( PL_get_atom(new, &a) && a == ATOM_very_deep)
- { debugstatus.skiplevel = VERY_DEEP;
- succeed;
- }
-
- fail;
- }
-
- word
- pl_spy(term_t p)
- { Procedure proc;
-
- if ( get_procedure(p, &proc, 0, GP_FIND) )
- { set(proc->definition, SPY_ME);
- return pl_debug();
- }
-
- fail;
- }
-
- word
- pl_nospy(term_t p)
- { Procedure proc;
-
- if ( get_procedure(p, &proc, 0, GP_FIND) )
- { clear(proc->definition, SPY_ME);
- succeed;
- }
-
- fail;
- }
-
- word
- pl_leash(term_t old, term_t new)
- { return setInteger(&debugstatus.leashing, "$leash", old, new);
- }
-
- word
- pl_visible(term_t old, term_t new)
- { return setInteger(&debugstatus.visible, "$visible", old, new);
- }
-
-
- word
- pl_debuglevel(term_t old, term_t new)
- { return setInteger(&GD->debug_level, "$debuglevel", old, new);
- }
-
-
-
- word
- pl_unknown(term_t old, term_t new)
- { Module m = contextModule(environment_frame);
- atom_t a = (true(m, UNKNOWN) ? ATOM_trace : ATOM_fail);
-
- if ( !PL_unify_atom(old, a) )
- fail;
- if ( PL_get_atom(new, &a) )
- { if ( a == ATOM_fail )
- { clear(m, UNKNOWN);
- succeed;
- } else if ( a == ATOM_trace )
- { set(m, UNKNOWN);
- succeed;
- }
- }
-
- return warning("unknown/2: instantiation fault");
- }
-
-
- word
- pl_prolog_current_frame(term_t frame)
- { LocalFrame fr = environment_frame;
-
- if ( fr->predicate->definition.function == pl_prolog_current_frame )
- fr = parentFrame(fr); /* thats me! */
-
- return PL_unify_integer(frame, PrologRef(fr));
- }
-
-
- word
- pl_prolog_frame_attribute(term_t frame, term_t what,
- term_t value)
- { LocalFrame fr;
- atom_t key;
- int arity;
- term_t result = PL_new_term_ref();
- long fri;
-
- if ( !PL_get_long(frame, &fri) ||
- !PL_get_name_arity(what, &key, &arity) )
- { ierr:
- return warning("prolog_frame_attribute/3: instantiation fault");
- }
-
- if ((fr = FrameRef(fri)) < lBase || fr > lTop)
- return warning("prolog_frame_attribute/3: illegal frame reference");
-
- set(fr, FR_WATCHED); /* explicit call to do this? */
-
- if ( key == ATOM_argument && arity == 1 )
- { term_t arg = PL_new_term_ref();
- int argn;
- Word p = valTermRef(value);
-
- if ( !PL_get_arg(1, what, arg) || !PL_get_integer(arg, &argn) || argn < 1 )
- goto ierr;
-
- if ( true(fr->predicate, FOREIGN) || !fr->clause )
- { if ( argn > fr->predicate->functor->arity )
- fail;
- } else
- { if ( argn > fr->clause->clause->prolog_vars )
- fail;
- }
-
- #ifdef O_DEBUGLOCAL /* see pl-wam.c */
- assert( *argFrameP(fr, argn-1) != (word)(((char*)ATOM_nil) + 1) );
- checkData(argFrameP(fr, argn-1));
- #endif
-
- deRef(p);
- if ( isVar(*p) )
- { *p = makeRef(argFrameP(fr, argn-1));
- DoTrail(p);
- succeed;
- }
-
- fail;
- }
- if ( arity != 0 )
- goto ierr;
-
- if ( key == ATOM_level)
- { PL_put_integer(result, levelFrame(fr));
- } else if (key == ATOM_has_alternatives)
- { PL_put_atom(result, hasAlternativesFrame(fr) ? ATOM_true : ATOM_false);
- } else if (key == ATOM_alternative)
- { if (fr->backtrackFrame == (LocalFrame) NULL)
- fail;
- PL_put_integer(result, PrologRef(fr->backtrackFrame));
- } else if (key == ATOM_parent)
- { LocalFrame parent;
-
- if ( fr->parent )
- clearUninitialisedVarsFrame(fr->parent, fr->programPointer);
-
- if ( (parent = parentFrame(fr)) )
- PL_put_integer(result, PrologRef(parent));
- else
- fail;
- } else if (key == ATOM_top)
- { PL_put_atom(result, fr->parent ? ATOM_false : ATOM_true);
- } else if (key == ATOM_context_module)
- { PL_put_atom(result, contextModule(fr)->name);
- } else if (key == ATOM_clause)
- { if ( false(fr->predicate, FOREIGN) && fr->clause )
- PL_put_pointer(result, fr->clause->clause);
- else
- fail;
- } else if (key == ATOM_goal)
- { int arity, n;
- term_t arg = PL_new_term_ref();
-
- if (fr->predicate->module != MODULE_user)
- { PL_put_functor(result, FUNCTOR_module2);
- PL_get_arg(1, result, arg);
- PL_unify_atom(arg, fr->predicate->module->name);
- PL_get_arg(2, result, arg);
- } else
- PL_put_term(arg, result);
-
- if ((arity = fr->predicate->functor->arity) == 0)
- { PL_unify_atom(arg, fr->predicate->functor->name);
- } else
- { term_t a = PL_new_term_ref();
-
- PL_unify_functor(arg, fr->predicate->functor->functor);
- for(n=0; n < arity; n++)
- { PL_get_arg(n+1, arg, a);
- unify_ptrs(valTermRef(a), argFrameP(fr, n));
- }
- }
- } else if ( key == ATOM_pc )
- { if ( fr->programPointer &&
- fr->parent &&
- false(fr->parent->predicate, FOREIGN) )
- PL_put_integer(result,
- fr->programPointer - fr->parent->clause->clause->codes);
- else
- fail;
- } else if ( key == ATOM_hidden )
- { atom_t a;
-
- if ( SYSTEM_MODE )
- { a = ATOM_true;
- } else
- { if ( true(fr, FR_NODEBUG) || false(fr->predicate, TRACE_ME) )
- a = ATOM_true;
- else
- a = ATOM_false;
- }
-
- PL_put_atom(result, a);
- } else
- return warning("prolog_frame_attribute/3: unknown key");
-
- return PL_unify(value, result);
- }
-
-
- #if O_DEBUGGER
-
- /*******************************
- * PROLOG EVENT HOOK *
- *******************************/
-
- void
- callEventHook(int ev, ...)
- { if ( !PROCEDURE_event_hook1 )
- PROCEDURE_event_hook1 = PL_predicate("prolog_event_hook", 1, "user");
-
- if ( PROCEDURE_event_hook1->definition->definition.clauses )
- { va_list args;
- fid_t fid = PL_open_foreign_frame();
- term_t arg = PL_new_term_ref();
-
- va_start(args, ev);
- switch(ev)
- { case PLEV_ERASED:
- { void *ptr = va_arg(args, void *); /* object erased */
-
- PL_unify_term(arg, PL_FUNCTOR, FUNCTOR_erased1,
- PL_POINTER, ptr);
- break;
- }
- case PLEV_DEBUGGING:
- { int dbg = va_arg(args, int);
-
- PL_unify_term(arg, PL_FUNCTOR, FUNCTOR_debugging1,
- PL_ATOM, dbg ? ATOM_true : ATOM_false);
- break;
- }
- case PLEV_TRACING:
- { int trc = va_arg(args, int);
-
- PL_unify_term(arg, PL_FUNCTOR, FUNCTOR_tracing1,
- PL_ATOM, trc ? ATOM_true : ATOM_false);
- break;
- }
- case PLEV_BREAK:
- case PLEV_NOBREAK:
- { Clause clause = va_arg(args, Clause);
- int offset = va_arg(args, int);
-
- PL_unify_term(arg, PL_FUNCTOR, FUNCTOR_break3,
- PL_POINTER, clause,
- PL_INTEGER, offset,
- PL_ATOM, ev == PLEV_BREAK ? ATOM_true
- : ATOM_false);
- break;
- }
- case PLEV_FRAMEFINISHED:
- { LocalFrame fr = va_arg(args, LocalFrame);
-
- PL_unify_term(arg, PL_FUNCTOR, FUNCTOR_frame_finished1,
- PL_INTEGER, PrologRef(fr));
- break;
- }
- default:
- warning("callEventHook(): unknown event: %d", ev);
- goto out;
- }
-
- PL_call_predicate(MODULE_user, FALSE, PROCEDURE_event_hook1, arg);
- out:
- PL_discard_foreign_frame(fid);
- va_end(args);
- }
- }
-
- #endif /*O_DEBUGGER*/
-